﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading;
using SKYPE4COMLib;

namespace CamPlug.SkypeConference.Plugin
{
	public class CallMonitor
	{
		object SyncRoot = new object();
		DateTime? StartTalkingTime;
		DateTime? StartWaitingToTalkTime;
		Thread MonitorThread;
		Skype SkypeClient;
		bool IsAttemptingConnection;
		TCallStatus LastKnownCallStatus;

		public CallMonitor(Skype skypeClient)
		{
			this.SkypeClient = skypeClient;
			(this.SkypeClient as _ISkypeEvents_Event).CallStatus += CallMonitor_CallStatus;
			AttachSkypeClient();
			EnsureIsMonitoring();
		}

		public TimeSpan CurrentTalkingDuration
		{
			get
			{
				lock (SyncRoot)
				{
					if (!StartTalkingTime.HasValue)
						return TimeSpan.Zero;
					else
						return DateTime.UtcNow - StartTalkingTime.Value;
				}
			}
		}

		TimeSpan _totalTalkingDuration;
		public TimeSpan TotalTalkingDuration
		{
			get
			{
				lock (SyncRoot)
					return _totalTalkingDuration + CurrentTalkingDuration;
			}
			private set
			{
				lock (SyncRoot)
					_totalTalkingDuration = value;
			}
		}

		public TimeSpan CurrentWaitingToTalkDuration
		{
			get
			{
				lock (SyncRoot)
				{
					if (!StartWaitingToTalkTime.HasValue)
						return TimeSpan.Zero;
					else
						return DateTime.UtcNow - StartWaitingToTalkTime.Value;
				}
			}
		}

		TimeSpan _totalWaitingToTalkDuration;
		public TimeSpan TotalWaitingToTalkDuration
		{
			get
			{
				lock (SyncRoot)
					return _totalWaitingToTalkDuration + CurrentWaitingToTalkDuration;
			}
			private set
			{
				lock (SyncRoot)
					_totalWaitingToTalkDuration = value;
			}
		}

		CallMonitorStatus _status;
		public CallMonitorStatus Status
		{
			get
			{
				lock (SyncRoot)
					return _status;
			}

			set
			{
				lock (SyncRoot)
				{
					if (value == _status)
						return;

					switch (Status)
					{
						case CallMonitorStatus.AttachedButNotInCall:
							break;

						case CallMonitorStatus.Talking:
							StoppedTalking();
							break;

						case CallMonitorStatus.WaitingToTalk:
							StoppedWaitingToTalk();
							break;
					}

					_status = value;

					switch (Status)
					{
						case CallMonitorStatus.Talking:
							StartedToTalk();
							break;

						case CallMonitorStatus.WaitingToTalk:
							if (!IsInCall)
								Status = CallMonitorStatus.AttachedButNotInCall;
							else
								StartedWaitingToTalk();
							break;

						case CallMonitorStatus.AttachedButNotInCall:
							break;
					}
				}
			}
		}

		public bool IsInCall
		{
			get
			{
				try
				{
					if ((SkypeClient as ISkype).AttachmentStatus != TAttachmentStatus.apiAttachSuccess)
						return false;

					if (SkypeClient.ActiveCalls.Count < 1)
						return false;

					return LastKnownCallStatus == TCallStatus.clsInProgress;
				}
				catch
				{
					return false;
				}
			}
		}

		public void ResetStatistics()
		{
			lock (SyncRoot)
			{
				_totalTalkingDuration = TimeSpan.Zero;
				_totalWaitingToTalkDuration = TimeSpan.Zero;
			}
		}

		public void Dispose()
		{
			lock (SyncRoot)
				StopMonitoring();
		}

		void StartedToTalk()
		{
			lock (SyncRoot)
			{
				UnMuteCall();
				StartTalkingTime = DateTime.UtcNow;
			}
		}

		void StoppedTalking()
		{
			lock (SyncRoot)
			{
				MuteCall();
				_totalTalkingDuration += CurrentTalkingDuration;
				StartTalkingTime = null;
			}
		}

		void StartedWaitingToTalk()
		{
			lock (SyncRoot)
			{
				MuteCall();
				StartWaitingToTalkTime = DateTime.UtcNow;
			}
		}

		void StoppedWaitingToTalk()
		{
			lock (SyncRoot)
			{
				_totalWaitingToTalkDuration += CurrentWaitingToTalkDuration;
				StartWaitingToTalkTime = null;
			}
		}

		void EnsureIsMonitoring()
		{
			lock (SyncRoot)
			{
				if (MonitorThread != null)
					return;

				MuteCall();
				MonitorThread = new Thread(new ThreadStart(Execute));
			}
			MonitorThread.Start();
		}

		void MuteCall()
		{
			if (!IsInCall)
				return;
			((ISkype)SkypeClient).Mute = false;
			((ISkype)SkypeClient).Mute = true;
		}

		void UnMuteCall()
		{
			if (!IsInCall)
				return;
			((ISkype)SkypeClient).Mute = true;
			((ISkype)SkypeClient).Mute = false;
		}

		void StopMonitoring()
		{
			lock (SyncRoot)
			{
				if (MonitorThread != null)
					MonitorThread = null;
			}
		}

		void UpdateStatus()
		{
			lock (SyncRoot)
			{
				if (MonitorThread != Thread.CurrentThread)
					return;

				switch ((SkypeClient as ISkype).AttachmentStatus)
				{
					case TAttachmentStatus.apiAttachSuccess:
						break;

					case TAttachmentStatus.apiAttachRefused:
						Status = CallMonitorStatus.AttachRequestRefused;
						return;

					case TAttachmentStatus.apiAttachNotAvailable:
						Status = CallMonitorStatus.NotAttached;
						return;

					case TAttachmentStatus.apiAttachUnknown:
						Status = CallMonitorStatus.NotAttached;
						return;

					case TAttachmentStatus.apiAttachPendingAuthorization:
						Status = CallMonitorStatus.AttachRequestPending;
						return;

					case TAttachmentStatus.apiAttachAvailable:
						AttachSkypeClient();
						return;
				}

				if (!IsInCall)
				{
					Status = CallMonitorStatus.AttachedButNotInCall;
					return;
				}
				if (((ISkype)SkypeClient).Mute)
				{
					if (Status != CallMonitorStatus.WaitingToTalk)
						Status = CallMonitorStatus.Listening;
					return;
				}
				Status = CallMonitorStatus.Talking;
			}
		}

		void AttachSkypeClient()
		{
			if (IsAttemptingConnection)
				return;

			LastKnownCallStatus = TCallStatus.clsUnknown;
			try
			{
				IsAttemptingConnection = true;
				SkypeClient.Attach(9, false);
				if ((SkypeClient as ISkype).AttachmentStatus != TAttachmentStatus.apiAttachSuccess)
					return;
				if (SkypeClient.ActiveCalls.Count > 0)
					LastKnownCallStatus = TCallStatus.clsInProgress;
			}
			catch { }
			finally
			{
				IsAttemptingConnection = false;
			}

		}

		void Execute()
		{
			while (MonitorThread == Thread.CurrentThread)
			{
				Thread.Sleep(250);
				UpdateStatus();
			}
		}

		void CallMonitor_CallStatus(Call pCall, TCallStatus status)
		{
			LastKnownCallStatus = status;
			if (status == TCallStatus.clsInProgress)
				MuteCall();
		}


	}
}
